/* PROJECT:         Lite sndrec32
 * LICENSE:         GPL - See COPYING in the top level directory
 * FILE:            base/applications/sndrec32/audio_resampler_acm.cpp
 * PURPOSE:         Sound recording
 * PROGRAMMERS:     Marco Pagliaricci (irc: rendar)
 */


#include "stdafx.h"
#include "audio_resampler_acm.hpp"
//#include <stdio.h>



_AUDIO_NAMESPACE_START_


/////////////////////////////////////////
///////     Private Functions    ////////
/////////////////////////////////////////


void
audio_resampler_acm::init_( void )
{



    //
    // Zeroing structures
    //

    ZeroMemory( &acm_header, sizeof( ACMSTREAMHEADER ));
    ZeroMemory( &wformat_src, sizeof( WAVEFORMATEX ));
    ZeroMemory( &wformat_dst, sizeof( WAVEFORMATEX ));


    
    
    //
    // Setting structures sizes
    //

    acm_header.cbStruct = sizeof( ACMSTREAMHEADER );
    wformat_src.cbSize = sizeof( WAVEFORMATEX );
    wformat_dst.cbSize = sizeof( WAVEFORMATEX );




    //
    // Setting WAVEFORMATEX structure parameters
    // according to `audio_format' in/out classes
    //

    wformat_src.wFormatTag = WAVE_FORMAT_PCM;
    wformat_src.nSamplesPerSec = audfmt_in.sample_rate();
    wformat_src.nChannels = audfmt_in.channels();
    wformat_src.wBitsPerSample = audfmt_in.bits();
    wformat_src.nAvgBytesPerSec =  audfmt_in.byte_rate();
    wformat_src.nBlockAlign = audfmt_in.block_align();


    wformat_dst.wFormatTag = WAVE_FORMAT_PCM;
    wformat_dst.nSamplesPerSec = audfmt_out.sample_rate();
    wformat_dst.nChannels = audfmt_out.channels();
    wformat_dst.wBitsPerSample = audfmt_out.bits();
    wformat_dst.nAvgBytesPerSec =  audfmt_out.byte_rate();
    wformat_dst.nBlockAlign = audfmt_out.block_align();



    //
    // Init acm structures completed successfull
    //
}











/////////////////////////////////////////
///////     Public Functions     ////////
/////////////////////////////////////////




void
audio_resampler_acm::open( void )
{


    MMRESULT err;


    //
    // Opens ACM stream
    //

    err = acmStreamOpen( &acm_stream, 0, &wformat_src, &wformat_dst, 
                    0, 0, 0, ACM_STREAMOPENF_NONREALTIME );


    if ( err != MMSYSERR_NOERROR )
    {
        //TODO: throw error
        MessageBox( 0, _T("acmOpen error: %i"), _T("ERROR"), MB_ICONERROR );

    }



    //
    // Calcs source buffer length
    //

    src_buflen = ( unsigned int )
        (( float )audfmt_in.byte_rate() * ( float )buf_secs );


    
    
    
    
    //
    // Calcs destination source buffer length
    // with help of ACM apis
    //

    err = acmStreamSize( acm_stream, 
        src_buflen, &dst_buflen, ACM_STREAMSIZEF_SOURCE );
    

    if ( err != MMSYSERR_NOERROR )
    {
        //TODO: throw error
        MessageBox( 0, _T("acmStreamSize error"), _T("ERROR"), MB_ICONERROR );


    }



    //
    // Initialize ACMSTREAMHEADER structure,
    // and alloc memory for source and destination
    // buffers.
    //

    acm_header.fdwStatus = 0;
    acm_header.dwUser = 0;

    
    acm_header.pbSrc = ( LPBYTE ) new BYTE [ src_buflen ];
    acm_header.cbSrcLength = src_buflen;
    acm_header.cbSrcLengthUsed = 0;
    acm_header.dwSrcUser = src_buflen;


    acm_header.pbDst = ( LPBYTE ) new BYTE [ dst_buflen ];
    acm_header.cbDstLength = dst_buflen;
    acm_header.cbDstLengthUsed = 0;
    acm_header.dwDstUser = dst_buflen;




    //
    // Give ACMSTREAMHEADER initialized correctly to the
    // driver.
    //

    err = acmStreamPrepareHeader( acm_stream, &acm_header, 0L );

    if ( err != MMSYSERR_NOERROR )
    {
        //TODO: throw error
        MessageBox( 0, _T("acmStreamPrepareHeader error"), _T("ERROR"), MB_ICONERROR );


    }




    //
    // ACM stream successfully opened.
    //

    stream_opened = true;

}




void
audio_resampler_acm::close( void )
{


    MMRESULT err;


    if ( acm_stream )
    {

        if ( acm_header.fdwStatus & ACMSTREAMHEADER_STATUSF_PREPARED )
        {

            acm_header.cbSrcLength = src_buflen;
            acm_header.cbDstLength = dst_buflen;

            err = acmStreamUnprepareHeader( acm_stream, &acm_header, 0L );

        
            if ( err != MMSYSERR_NOERROR )
            {
            
                //
                // Free buffer memory
                //

                if ( acm_header.pbSrc != 0 )
                    delete[] acm_header.pbSrc;

                if ( acm_header.pbDst != 0 )
                    delete[] acm_header.pbDst;


                //
                // Re-init structures
                //

                init_();

                //
                // Updating status
                //

                stream_opened = false;


                //TODO: throw error
                MessageBox( 0, _T("acmStreamUnPrepareHeader error"), _T("ERROR"), MB_ICONERROR );

            
            }
        }


        err = acmStreamClose( acm_stream, 0 );
        acm_stream = 0;

        if ( err != MMSYSERR_NOERROR )
        {

            //
            // Free buffer memory
            //

            if ( acm_header.pbSrc != 0 )
                delete[] acm_header.pbSrc;

            if ( acm_header.pbDst != 0 )
                delete[] acm_header.pbDst;


            //
            // Re-init structures
            //

            init_();


            //
            // Updating status
            //

            stream_opened = false;


            //TODO: throw error!

            MessageBox( 0, _T("acmStreamClose error"), _T("ERROR"), MB_ICONERROR );


        }


    }//if acm_stream != 0






    //
    // Free buffer memory
    //

    if ( acm_header.pbSrc != 0 )
        delete[] acm_header.pbSrc;

    if ( acm_header.pbDst != 0 )
        delete[] acm_header.pbDst;


    //
    // Re-init structures
    //

    init_();


    //
    // Updating status
    //

    stream_opened = false;



    //
    // ACM sream successfully closed.
    //


}




void 
audio_resampler_acm::audio_receive( unsigned char * data, unsigned int size )
{

    MMRESULT err;

    //
    // Checking for acm stream opened
    //

    if ( stream_opened )
    {

        
        //
        // Copy audio data from extern to
        // internal source buffer
        //

        memcpy( acm_header.pbSrc, data, size );


        acm_header.cbSrcLength = size;
        acm_header.cbDstLengthUsed = 0;

        err = acmStreamConvert( acm_stream, &acm_header, ACM_STREAMCONVERTF_BLOCKALIGN );

        if ( err != MMSYSERR_NOERROR )
        {
            //TODO: throw error
            MessageBox( 0, _T("acmStreamConvert error"), _T("ERROR"), MB_ICONERROR );


        }


        //
        // Wait for sound conversion
        //

        while(( ACMSTREAMHEADER_STATUSF_DONE & acm_header.fdwStatus ) == 0 );
    

        


        //
        // Copy resampled audio, to destination buffer.
        //

        //memcpy( pbOutputData, acm_header.pbDst, acm_header.cbDstLengthUsed );


    }

}










_AUDIO_NAMESPACE_END_
